package com.warren.aop.framework.autoproxy;

import com.warren.aop.*;
import com.warren.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.warren.aop.framework.ProxyFactory;
import com.warren.beans.PropertyValues;
import com.warren.beans.factory.BeanFactory;
import com.warren.beans.factory.BeanFactoryAware;
import com.warren.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.warren.beans.factory.support.DefaultListableBeanFactory;
import com.warren.error.BeansException;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @author warren
 * @create 2021-08-26 16:07
 * @Description BeanPostProcessor 实现，它根据当前 BeanFactory 中的所有候选顾问创建 AOP 代理。
 * 这个类是完全通用的；它不包含处理任何特定方面的特殊代码，例如池化方面。(融入Bean生命周期的自动代理创建者)
 */
public class DefaultAdvisorAutoProxyCreator  implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    private final Set<Object> earlyProxyReferences = Collections.synchronizedSet(new HashSet<Object>());

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        //if (isInfracstructureClass(beanClass)) return null;
        ////beanFactory.getBeansOfType 获取 AspectJExpressionPointcutAdvisor 开始
        //Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
        //for (AspectJExpressionPointcutAdvisor advisor : advisors){
        //    ClassFilter classFilter = advisor.getPointcut().getClassFilter();
        //    if (!classFilter.matches(beanClass)) continue;
        //
        //    AdvisedSupport advisedSupport = new AdvisedSupport();
        //    TargetSource targetSource = null;
        //
        //    try {
        //        targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
        //    } catch (Exception e) {
        //        e.printStackTrace();
        //    }
        //    advisedSupport.setTargetSource(targetSource);
        //    advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
        //    advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
        //    advisedSupport.setProxyTargetClass(false);
        //
        //    return new ProxyFactory(advisedSupport).getProxy();
        //}
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }


    private boolean isInfrastructureClass(Class<?> beanClass){
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (!earlyProxyReferences.contains(beanName)) {
            return wrapIfNecessary(bean, beanName);
        }
        return bean;
    }

    /**
     * 是否有封装必要
     * @param bean
     * @param beanName
     * @return
     */
    protected Object wrapIfNecessary(Object bean, String beanName) {
        if (isInfrastructureClass(bean.getClass())) return bean;

        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            // 过滤匹配类
            if (!classFilter.matches(bean.getClass())) continue;

            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = new TargetSource(bean);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(true);

            // 返回代理对象
            return new ProxyFactory(advisedSupport).getProxy();
        }

        return bean;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        earlyProxyReferences.add(beanName);
        return wrapIfNecessary(bean, beanName);
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }


}
